﻿using System;
using System.Collections;
using Blaze.Framework;
using UnityEngine;

/// <summary>
/// 协同程序管理器，用于提供非<see cref="MonoBehaviour"/>的协同程序使用。
/// </summary>
public static class CoroutineManager
{
    /// <summary>
    /// 用于执行协同程序的<see cref="MonoBehaviour"/>。
    /// </summary>
    [Singleton]
    public class CoroutineRunner : MonoBehaviour
    {
    }

    /// <summary>
    /// 在下一帧执行指定的方法。
    /// </summary>
    /// <param name="action">需要指定的方法</param>
    public static Coroutine ExecuteOnNextFrame(Action action)
    {
        return mRunner.StartCoroutine(frameDelayExecute(1, action));
    }

    /// <summary>
    /// 按帧数延迟执行一个方法。
    /// </summary>
    /// <param name="frame">需要延迟的帧数</param>
    /// <param name="action">需要执行的方法</param>
    public static Coroutine FrameDelayExecute(int frame, Action action)
    {
        return mRunner.StartCoroutine(frameDelayExecute(frame, action));
    }

    /// <summary>
    /// 初始化管理器。
    /// </summary>
    [RuntimeInitializeOnLoadMethod]
    public static void Initialize()
    {
        if (mIsInitialized)
            return;
        mRunner = Singleton.Get<CoroutineRunner>();
        mIsInitialized = true;
    }

    /// <summary>
    /// 启动一个新的协同程序。
    /// </summary>
    /// <param name="coroutine">协同程序所使用的迭代器</param>
    /// <returns>创建的协同程序</returns>
    public static Coroutine StartNew(IEnumerator coroutine)
    {
        return mRunner.StartCoroutine(coroutine);
    }

    /// <summary>
    /// 停止指定的协同程序。
    /// </summary>
    /// <param name="coroutine">需要停止的协同程序</param>
    public static void Stop(Coroutine coroutine)
    {
        mRunner.StopCoroutine(coroutine);
    }

    /// <summary>
    /// 按秒数延迟执行一个方法。
    /// </summary>
    /// <param name="seconds">需要延迟的秒数</param>
    /// <param name="ignoreTimeScale">是否要忽略Time.timeScale</param>
    /// <param name="action">需要执行的方法</param>
    public static Coroutine TimeDelayExecute(float seconds, bool ignoreTimeScale, Action action)
    {
        return mRunner.StartCoroutine(timeDelayExecute(seconds, ignoreTimeScale, action));
    }

    private static IEnumerator frameDelayExecute(int frame, Action action)
    {
        for (var i = 0; i < frame; i++)
            yield return null;
        action();
    }

    private static IEnumerator timeDelayExecute(float seconds, bool ignoreTimeScale, Action action)
    {
        if (ignoreTimeScale)
            yield return new WaitForSecondsRealtime(seconds);
        else
            yield return new WaitForSeconds(seconds);
        action();
    }

    private static CoroutineRunner mRunner;
    private static bool mIsInitialized;
}